package com.modularity.camera.basic.camera1;

import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Looper;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.modularity.camera.basic.R;
import com.modularity.camera.basic.base.CameraFragment;
import com.modularity.camera.basic.bean.Size;
import com.modularity.camera.basic.camera2.AutoFitTextureView;
import com.modularity.camera.basic.enums.CameraFace;
import com.modularity.camera.basic.enums.CameraQuality;
import com.modularity.camera.basic.listener.ICBTakePictureListener;
import com.modularity.common.utils.managers.manager.ThreadManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Camera1BasicTVFragment extends CameraFragment implements Camera.PreviewCallback {

    private static final String TAG = "Camera1BasicTVFragment";

    private AutoFitTextureView mTextureView;

    //        private int mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
    private int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
    private Camera mCamera;
    private Camera.Parameters mParameters;

    private int mWidth;
    private int mHeight;

    //    private File mPicturesPath = null;
    private int mOrientation;
    private Camera.Size mPreviewSize;


    /**
     * Max preview width that is guaranteed by Camera2 API
     */
    private static final int MAX_PREVIEW_WIDTH = 1920;

    /**
     * Max preview height that is guaranteed by Camera2 API
     */
    private static final int MAX_PREVIEW_HEIGHT = 1080;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.cb_fragment_camera1_basic_tv, container, false);
    }

    @Override
    public void onViewCreated(final View view, Bundle savedInstanceState) {
        mTextureView = view.findViewById(R.id.camera_t_view);
        if (isCustomRotate()) {
            mTextureView.setRotation(getRotateValue());
        }
        if (isCustomScaleX()){
            mTextureView.setScaleX(getScaleX());
        }
        if (isCustomScaleY()){
            mTextureView.setScaleY(getScaleY());
        }
        init();
    }


    private void initParameters(final Camera camera) {
        mParameters = camera.getParameters();
        mParameters.setPreviewFormat(ImageFormat.NV21); //default
        mParameters.getSupportedPreviewFormats();
        mParameters.getSupportedPictureFormats();
        if (isSupportFocus(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        } else if (isSupportFocus(Camera.Parameters.FOCUS_MODE_AUTO)) {
            mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        if (isCustomPreview()) {
            setCustomPreviewSize();
        } else {
            setPreviewSize();
        }
        if (isCustomPicture()) {
            setCustomPictureSize();
        } else {
            setPictureSize();
        }
//        mParameters.set("preview-flip", "flip-h");
        camera.setParameters(mParameters);
    }

    private void startPreview() {
        try {
            mCamera.setPreviewTexture(mTextureView.getSurfaceTexture());
            setCameraDisplayOrientation();
            mCamera.startPreview();
//            startFaceDetect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void startFaceDetect() {
        mCamera.startFaceDetection();
        mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() {
            @Override
            public void onFaceDetection(Camera.Face[] faces, Camera camera) {
                Log.d(TAG, "face length: " + faces.length);
            }
        });
    }

    private void init() {
//        mPicturesPath = getActivity().getExternalCacheDir();

        ThreadManager.runOnUiThreadDelayed(new Runnable() {
            @Override
            public void run() {
                if (null == mCamera) {
                    openCamera(mCameraId);
                }
                startPreview();

                DisplayMetrics dm = new DisplayMetrics();
                getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
                mWidth = dm.widthPixels;
                mHeight = dm.heightPixels;

                Log.d(TAG, "screen width: " + mWidth + ", height: " + mHeight);
            }
        },100);

    }


    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {

    }

    private ICBTakePictureListener mTakePictureListener;
    private File mPictureFile;

    @Override
    public void takePicture(String fileName, ICBTakePictureListener listener) {
        mTakePictureListener = listener;
        if (!TextUtils.isEmpty(fileName)) {
            mPictureFile = new File(fileName);
        } else {
            mPictureFile = new File(getActivity().getExternalFilesDir(null), System.currentTimeMillis() + ".jpg");
        }
        takePicture();
    }

    @Override
    public void takePicture(ICBTakePictureListener listener) {
        takePicture("", listener);
    }

    private void takePicture() {
        if (null != mCamera) {
            mCamera.takePicture(new Camera.ShutterCallback() {
                @Override
                public void onShutter() {

                }
            }, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //base data
                }
            }, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(final byte[] data, Camera camera) {
                    mCamera.startPreview();
                    new Thread(
                            new Runnable() {
                                @Override
                                public void run() {
                                    savePicture(data);
                                }
                            }
                    ).start();
                }
            });
        }
    }

    private void savePicture(final byte[] data) {
        if (mPictureFile != null && !mPictureFile.getParentFile().exists()) {
            mPictureFile.getParentFile().mkdirs();
        }
//        File file = new File(mPicturesPath, System.currentTimeMillis() + ".jpg");
        if (!mPictureFile.exists()) {
            try {
                mPictureFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(mPictureFile);
            fos.write(data);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        rotateImageView(mCameraId, mOrientation, mPictureFile.getPath());
    }

    private void rotateImageView(int cameraId, int orientation, String path) {
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        Bitmap resizeBitmap = null;
        switch (cameraId) {
            case Camera.CameraInfo.CAMERA_FACING_BACK:
                Matrix matrix = new Matrix();
                if (orientation == 90) {
                    matrix.postRotate(90);
                }
                resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                break;
            case Camera.CameraInfo.CAMERA_FACING_FRONT:
                Matrix m = new Matrix();
                m.postScale(-1f, 1f);
                resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
                break;
        }
        File file = new File(path);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            if (cameraQuality == CameraQuality.QUALITY_LOW) {
                resizeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, fos);
            } else if (cameraQuality == CameraQuality.QUALITY_MEDIUM) {
                resizeBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos);
            } else {
                resizeBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            }
            fos.flush();
            fos.close();
            bitmap.recycle();
            resizeBitmap.recycle();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Looper.prepare();
//        Toast.makeText(this.getActivity(), "save file path: " + path, Toast.LENGTH_LONG).show();
        if (mTakePictureListener != null) {
            mTakePictureListener.onTakePicture(file);
        }
        Looper.loop();
    }

    public void openCamera(final int cameraId) {
//        if (isSupport(cameraId)) {
//            try {
//                mCamera = Camera.open(cameraId);
//                initParameters(mCamera);
//                //preview
//                if (null != mCamera) {
//                    mCamera.setPreviewCallback(this);
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        } else {
//            int cameraCount = Camera.getNumberOfCameras();
//            Toast.makeText(getContext(), "不支持当前CameraId:" + cameraId + " cameraCount:" + cameraCount, Toast.LENGTH_LONG).show();
//        }
        try {
            mCamera = Camera.open(cameraId);
            initParameters(mCamera);
            //preview
            if (null != mCamera) {
                mCamera.setPreviewCallback(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
            messageReport(e.getMessage());
            Toast.makeText(getContext(), "不支持当前CameraId:" + cameraId, Toast.LENGTH_LONG).show();
        }
    }


    @Override
    public void switchCamera() {
        releaseCamera();
        if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            mCameraFace = CameraFace.FACING_BACK;
        } else {
            mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
            mCameraFace = CameraFace.FACING_FRONT;
        }
        openCamera(mCameraId);
        startPreview();
    }

    @Override
    public void releaseCamera() {
        if (null != mCamera) {
            mCamera.stopPreview();
//            mCamera.stopFaceDetection();
            mCamera.setPreviewCallback(null);
            mCamera.release();
            mCamera = null;
        }
    }

    private void setCustomPictureSize() {
        Size customPictureSize = getCustomPictureSize();
        mParameters.setPictureSize(customPictureSize.getWidth(), customPictureSize.getHeight());
    }

    private void setCustomPreviewSize() {
        Size customPreviewSize = getCustomPreviewSize();
        mParameters.setPreviewSize(customPreviewSize.getWidth(), customPreviewSize.getHeight());
        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mTextureView.setAspectRatio(customPreviewSize.getWidth(), customPreviewSize.getHeight());
        } else {
            mTextureView.setAspectRatio(customPreviewSize.getHeight(), customPreviewSize.getWidth());
        }
    }

    @Override
    public List<Size> supportedPictureSizes() {
        if (mParameters != null) {
            List<Camera.Size> sizes = mParameters.getSupportedPictureSizes();
            List<Size> sizeList = new ArrayList<>();
            for (int i = 0; i < sizes.size(); i++) {
                Size item = new Size(sizes.get(i).width, sizes.get(i).height);
                sizeList.add(item);
            }
            return sizeList;
        }
        return new ArrayList<>();
    }

    @Override
    public List<Size> supportedPreviewSizes() {
        if (mParameters != null) {
            List<Camera.Size> sizes = mParameters.getSupportedPreviewSizes();
            List<Size> sizeList = new ArrayList<>();
            for (int i = 0; i < sizes.size(); i++) {
                Size item = new Size(sizes.get(i).width, sizes.get(i).height);
                sizeList.add(item);
            }
            return sizeList;
        }
        return new ArrayList<>();
    }

    private void setPreviewSize() {
        List<Camera.Size> supportSizes = mParameters.getSupportedPreviewSizes();
//        List<Camera.Size> supportSizes = mParameters.getSupportedPictureSizes();
        Camera.Size biggestSize = null;
//        Camera.Size fitSize = null;
        Camera.Size targetSize = null;
        Camera.Size targetSiz2 = null;
        if (null != supportSizes) {
            for (int i = 0; i < supportSizes.size(); i++) {
                Camera.Size size = supportSizes.get(i);
                Log.d(TAG, "SupportedPreviewSizes: width=" + size.width + ", height=" + size.height);
                if (biggestSize == null || (size.width >= biggestSize.width && size.height >= biggestSize.height)) {
                    biggestSize = size;
                    Log.d(TAG, "biggestSize: width=" + size.width + ", height=" + size.height);
                }
                if (size.width == mWidth && size.height == mHeight) {
                    mPreviewSize = size;
                    //如果任一宽或者高等于所支持的尺寸
                } else if (size.width == mWidth || size.height == mHeight) {
                    if (targetSize == null) {
                        targetSize = size;
                        //如果上面条件都不成立 如果任一宽高小于所支持的尺寸
                    } else if (size.width < mWidth || size.height < mHeight) {
                        targetSiz2 = size;
                    }
                }
            }
            if (mPreviewSize == null) {
                mPreviewSize = targetSize;
            }

            if (mPreviewSize == null) {
                mPreviewSize = targetSiz2;
            }

            if (mPreviewSize == null) {
                mPreviewSize = biggestSize;
            }

            int maxPreviewWidth = mWidth;
            int maxPreviewHeight = mHeight;

            if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
                maxPreviewWidth = MAX_PREVIEW_WIDTH;
            }

            if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
                maxPreviewHeight = MAX_PREVIEW_HEIGHT;
            }

            mPreviewSize = chooseOptimalSize(supportSizes, mTextureView.getWidth(), mTextureView.getHeight(), maxPreviewWidth, maxPreviewHeight, biggestSize);
            Log.d(TAG, "PreviewSize width: " + mPreviewSize.width + ", height: " + mPreviewSize.height);
            mParameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            int orientation = getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                mTextureView.setAspectRatio(mPreviewSize.width, mPreviewSize.height);
            } else {
                mTextureView.setAspectRatio(mPreviewSize.height, mPreviewSize.width);
            }
        }
    }

    private static Camera.Size chooseOptimalSize(List<Camera.Size> choices, int surfaceViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Camera.Size aspectRatio) {
        List<Camera.Size> bigEnough = new ArrayList<>();
        List<Camera.Size> notBigEnough = new ArrayList<>();
        int w = aspectRatio.width;
        int h = aspectRatio.height;
        for (Camera.Size option : choices) {
            if (option.width <= maxWidth && option.height <= maxHeight && option.height == option.width * h / w) {
                if (option.width >= surfaceViewWidth && option.height >= textureViewHeight) {
                    bigEnough.add(option);
                } else {
                    notBigEnough.add(option);
                }
            }
        }
        // Pick the smallest of those big enough. If there is no one big enough, pick the
        // largest of those not big enough.
        if (bigEnough.size() > 0) {
            return Collections.min(bigEnough, new Camera1BasicTVFragment.CompareSizesByArea());
        } else if (notBigEnough.size() > 0) {
            return Collections.max(notBigEnough, new Camera1BasicTVFragment.CompareSizesByArea());
        } else {
            return aspectRatio;
        }
    }

    /**
     * Compares two {@code Size}s based on their areas.
     */
    static class CompareSizesByArea implements Comparator<Camera.Size> {

        @Override
        public int compare(Camera.Size lhs, Camera.Size rhs) {
            return Long.signum((long) lhs.width * lhs.height - (long) rhs.width * rhs.height);
        }

    }

    /**
     * save picture size
     */
    private void setPictureSize() {
        List<Camera.Size> mPictureSizes = mParameters.getSupportedPictureSizes();
        Camera.Size previewSize = mParameters.getPreviewSize();
        Camera.Size biggestSize = null;
        Camera.Size fitSize = null;// 优先选预览界面的尺寸
        Log.d(TAG, "preview size: " + previewSize.width + ", height:" + previewSize.height);

        float scaleSize = 0;
        if (null != previewSize) {
            scaleSize = previewSize.width / (float) previewSize.height;
        }

        for (int i = 0; i < mPictureSizes.size(); i++) {
            Camera.Size picture = mPictureSizes.get(i);
            Log.d(TAG, "SupportedPictureSizes: width=" + picture.width + ", height="
                    + picture.height);
            if (null == biggestSize) {
                biggestSize = picture;
            } else if (picture.width > biggestSize.width && picture.height > biggestSize.height) {
                biggestSize = picture;
            }

            if (scaleSize > 0 && picture.width > previewSize.width && picture.height > previewSize.height) {
                float currentScale = picture.width / (float) picture.height;
                if (scaleSize == currentScale) {
                    if (null == fitSize) {
                        fitSize = picture;
                    } else if (picture.width > fitSize.width && picture.height > fitSize.height) {
                        fitSize = picture;
                    }
                }
            }
        }

        if (null == fitSize) {
            fitSize = biggestSize;
        }

        Log.d(TAG, "picture size: " + fitSize.width + ", height:" + fitSize.height);
        mParameters.setPictureSize(fitSize.width, fitSize.height);
    }


    private void setCameraDisplayOrientation() {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraId, cameraInfo);
        int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();  //自然方向
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        //cameraInfo.orientation 图像传感方向
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (cameraInfo.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (cameraInfo.orientation - degrees + 360) % 360;
        }
        Log.d(TAG, "CameraDisplayOrientation rotation: " + rotation
                + ", cameraInfo.orientation: " + cameraInfo.orientation + ", result: " + result);

        mOrientation = result;
        //相机预览方向
        mCamera.setDisplayOrientation(result);
    }

    private boolean isSupport(int backOrFront) {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == backOrFront) {
                return true;
            }
        }
        return false;
    }

    private boolean isSupportFocus(String focusMode) {
        for (String mode : mParameters.getSupportedFocusModes()) {
            if (focusMode.equals(mode)) {
                return true;
            }
        }

        return false;
    }

    private CameraFace mCameraFace;

    public static Camera1BasicTVFragment newInstance(CameraFace cameraFace) {
        return new Camera1BasicTVFragment(cameraFace);
    }

    public static Camera1BasicTVFragment newInstance(int cameraId) {
        return new Camera1BasicTVFragment(cameraId);
    }

    public Camera1BasicTVFragment(int cameraId) {
        mCameraFace = CameraFace.FACING_CUSTOM;
        mCameraId = cameraId;
    }

    public Camera1BasicTVFragment(CameraFace cameraFace) {
        mCameraFace = cameraFace;
        int cameraNum = Camera.getNumberOfCameras();
        if (cameraNum == 1) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(0, info);
            mCameraId = 0;
        } else if (cameraNum > 1) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            for (int i = 0; i < cameraNum; i++) {
                Camera.getCameraInfo(i, info);
                if (cameraFace == CameraFace.FACING_BACK && info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    mCameraId = i;
                    break;
                }
                if (cameraFace == CameraFace.FACING_FRONT && info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    mCameraId = i;
                    break;
                }
            }
        } else {
            mCameraId = -1;
        }
    }
}
